/*
 * soc_script.c
 *
 *  Created on: 2019年4月7日
 *      Author: 0yfj0
 */

#include <string.h>
#include "soc_script_types.h"
#include "soc_script_utils.h"
#include "soc_script_port.h"
#include "soc_script.h"

int soc_script_syntax_parse(soc_script_scb_t *scb, char *syntax);
int soc_script_exp_exec(soc_script_scb_t *scb, char *exp, int *bval);
int soc_script_val_get(soc_script_scb_t *scb, char *name, double *val);
int soc_script_val_set(soc_script_scb_t *scb, char *name, double *val);
int soc_script_io_get(char *name, double *val);
int soc_script_io_set(char *name, double *val);
int soc_script_scb_init(soc_script_scb_t *scb);
int soc_script_destroy(soc_script_scb_t *scb);

soc_script_scb_t *soc_script_func_load(char *buf, int size, char *func)
{
	int i;
	int len;
	char *ptr;
	char *exp;
	char *param;
	soc_script_scb_t *scb;

	if (!buf || size <= 0)
		return NULL;

	scb = soc_script_malloc(sizeof(soc_script_scb_t));
	if (scb != NULL)
	{
		soc_script_scb_init(scb);
		for (i = 0, ptr = buf, len = 0; i < size; ++i)
		{
			if (buf[i] == '\n')
			{
				exp = soc_script_exp_dup(ptr, len);
				if (exp != NULL)
				{
					if (soc_script_strncasecmp_s("def ", exp) == 0)
					{
						switch (scb->stat)
						{
						case SOC_SCRIPT_STAT_INIT:
							param = soc_script_trim(&exp[4]);
							if (strcmp(func, param) == 0)
							{
								scb->script = ptr;
								scb->stat = SOC_SCRIPT_STAT_FUNC_MACHED;
							}
							break;
						case SOC_SCRIPT_STAT_FUNC_MACHED:
							exp = scb->script;
							len = (int) (ptr - exp);
							scb->script = (char*) soc_script_malloc(len + 1);
							if (scb->script != NULL)
							{
								memcpy(scb->script, exp, len);
								scb->script[len] = '\0';
								scb->stat = SOC_SCRIPT_STAT_INIT;
								return scb;
							}
							soc_script_exp_free(exp);
							goto __return;
						default:
							break;
						}
					}
					soc_script_exp_free(exp);
				}
				ptr = &buf[i + 1];
				len = 0;
			}
			else
			{
				++len;
			}
		}

		exp = scb->script;
		len = (int) (&buf[size] - exp);
		scb->script = (char*) soc_script_malloc(len + 1);
		if (scb->script != NULL)
		{
			memcpy(scb->script, exp, len);
			scb->script[len] = '\0';
			scb->stat = SOC_SCRIPT_STAT_INIT;
			return scb;
		}
	}

	__return: if (scb != NULL)
		soc_script_free(scb);
	return NULL;
}

int soc_script_exec(soc_script_scb_t *scb)
{
	int i;
	int len;
	char *buf;
	char *exp;

	if (scb == NULL)
		return -1;

	if (scb->magic != SOC_SCRIPT_MAGIC)
		return -1;

	if (scb->script == NULL)
		return -1;

	buf = scb->script;
	exp = buf;
	for (i = 0, len = 0; buf[i] != '\0'; ++i)
	{
		if (buf[i] == '\n')
		{
			exp = soc_script_exp_dup(exp, len);
			if (exp != NULL)
			{
				if (soc_script_strncasecmp_s("def ", exp) != 0
						&& exp[0] == '\t')
				{
					soc_script_syntax_parse(scb, &exp[1]);
				}
				soc_script_exp_free(exp);
			}
		}
		else
		{
			++len;
		}
	}
	return 0;
}

void soc_script_del(soc_script_scb_t *scb)
{
	if (scb == NULL)
		return;

	if (scb->magic != SOC_SCRIPT_MAGIC)
		return;

	soc_script_destroy(scb);
	soc_script_free(scb);
}

int soc_script_func_exec(char *buf, int size, char *func)
{
	int i;
	int len;
	char *exp;
	char *param;
	soc_script_scb_t scb;

	if (buf == NULL || size <= 0)
		return -1;

	soc_script_scb_init(&scb);
	for (i = 0, exp = buf, len = 0; i < size; ++i)
	{
		if (buf[i] == '\n')
		{
			exp = soc_script_exp_dup(exp, len);
			if (exp != NULL)
			{
				if (soc_script_strncasecmp_s("def ", exp) == 0)
				{
					switch (scb.stat)
					{
					case SOC_SCRIPT_STAT_INIT:
						param = soc_script_trim(&exp[4]);
						if (strcmp(func, param) == 0)
							scb.stat = SOC_SCRIPT_STAT_FUNC_MACHED;
						break;
					case SOC_SCRIPT_STAT_FUNC_MACHED:
						scb.stat = SOC_SCRIPT_STAT_FUNC_END;
						break;
					case SOC_SCRIPT_STAT_FUNC_END:
					default:
						soc_script_exp_free(exp);
						goto __return;
					}
				}
				else if (scb.stat == SOC_SCRIPT_STAT_FUNC_MACHED)
				{
					if (exp[0] == '\t')
						soc_script_syntax_parse(&scb, &exp[1]);
				}
				else if (scb.stat == SOC_SCRIPT_STAT_FUNC_END)
				{
					soc_script_exp_free(exp);
					goto __return;
				}
				soc_script_exp_free(exp);
			}
			exp = &buf[i + 1];
			len = 0;
		}
		else
		{
			++len;
		}
	}

	if (len != 0)
	{
		exp = soc_script_exp_dup(exp, len);
		if (exp != NULL)
		{
			if (exp[0] == '\t')
				soc_script_syntax_parse(&scb, &exp[1]);
			soc_script_exp_free(exp);
		}
	}

	__return: soc_script_destroy(&scb);
	return scb.err;
}

int soc_script_syntax_parse(soc_script_scb_t *scb, char *syntax)
{
	int i;
	int len;
	int val;
	char *ptr;
	soc_script_var_t *var;

	if (!scb || !syntax)
		return -1;

	if (soc_script_strncasecmp_s("int ", syntax) == 0)
	{
		len = strlen(syntax);
		for (i = 4, ptr = &syntax[i]; i < len; ++i)
		{
			if (syntax[i] == ',')
			{
				syntax[i] = '\0';
				ptr = soc_script_trim(ptr);
				var = soc_script_var_malloc(ptr, SOC_SCRIPT_INT);
				soc_script_var_append(&scb->head, var);
				ptr = &syntax[i + 1];
			}
		}
		return 0;
	}
	else if (soc_script_strncasecmp_s("double ", syntax) == 0)
	{
		len = strlen(syntax);
		for (i = 7, ptr = &syntax[i]; i < len; ++i)
		{
			if (syntax[i] == ',')
			{
				syntax[i] = '\0';
				ptr = soc_script_trim(ptr);
				var = soc_script_var_malloc(ptr, SOC_SCRIPT_DOUBLE);
				soc_script_var_append(&scb->head, var);
				ptr = &syntax[i + 1];
			}
		}
		return 0;
	}
	else if (soc_script_strncasecmp_s("byte ", syntax) == 0)
	{
		len = strlen(syntax);
		for (i = 5, ptr = &syntax[i]; i < len; ++i)
		{
			if (syntax[i] == ',')
			{
				syntax[i] = '\0';
				ptr = soc_script_trim(ptr);
				var = soc_script_var_malloc(ptr, SOC_SCRIPT_BYTES);
				soc_script_var_append(&scb->head, var);
				ptr = &syntax[i + 1];
			}
		}
		return 0;
	}
	else if (soc_script_strncasecmp_s("if ", syntax) == 0)
	{
		ptr = strstr(syntax, ":");
		if (ptr != NULL)
		{
			*ptr = '\0';
			if (soc_script_exp_exec(scb, &syntax[3], &val) != 0)
				return -1;

			if (val != 0)
				return soc_script_exp_exec(scb, ptr + 1, NULL);
		}
	}
	else if (soc_script_strncasecmp_s("wait ", syntax) == 0)
	{
		ptr = strstr(syntax, ":");
		if (ptr != NULL)
		{
			*ptr = '\0';
			if (soc_script_char2bin(&syntax[5], &val) != 0)
				return -1;

			soc_script_wait(val);
			return soc_script_exp_exec(scb, ptr + 1, NULL);
		}
		else
		{
			if (soc_script_char2bin(&syntax[5], &val) != 0)
				return -1;
			soc_script_wait(val);
		}
	}
	else
	{
		return soc_script_exp_exec(scb, syntax, NULL);
	}

	return -1;
}

int soc_script_exp_exec(soc_script_scb_t *scb, char *exp, int *bval)
{
	char chr;
	int i;
	int j;
	int cnt;
	int len;
	double val0;
	double val1;
	char *ptr0;
	char *ptr1;
	char var0[16];
	char var1[16];

	if (!scb || !exp)
		return -1;

	memset(var0, 0, sizeof(var0));
	memset(var1, 0, sizeof(var1));
	len = strlen(exp);
	cnt = sizeof(var0) - 1;
	if (bval != NULL)/* 比较求值表达式[a>b,a>=b,a<b,a<=b,a==b] */
	{
		for (i = 0; i < cnt && i < len; ++i)
		{
			chr = exp[i];
			if (chr != '>' && chr != '<' && chr != '=')
				var0[i] = chr;
			else
				break;
		}
		for (j = 0; j < cnt && i < len; ++i)
		{
			chr = exp[i];
			if (chr != '>' && chr != '<' && chr != '=')
				var1[j++] = chr;
		}
		ptr0 = soc_script_trim(var0);
		if (soc_script_val_get(scb, ptr0, &val0) != 0)
			return -1;
		ptr1 = soc_script_trim(var1);
		if (soc_script_val_get(scb, ptr1, &val1) != 0)
			return -1;

		if (strstr(exp, "=="))
			*bval = (val0 == val1);
		else if (strstr(exp, ">="))
			*bval = (val0 >= val1);
		else if (strstr(exp, "<="))
			*bval = (val0 <= val1);
		else if (strstr(exp, "<"))
			*bval = (val0 < val1);
		else if (strstr(exp, ">"))
			*bval = (val0 > val1);
		else
			return -1;

		return 0;
	}
	else if (strchr(exp, '=') != NULL)/* 赋值表达式[a+=b,a-=b,a*=b,a/=b,a=b] */
	{
		for (i = 0; i < cnt && i < len; ++i)
		{
			chr = exp[i];
			if (chr != '+' && chr != '-' && chr != '*' && chr != '/'
					&& chr != '=')
				var0[i] = chr;
			else
				break;
		}
		for (j = 0; j < cnt && i < len; ++i)
		{
			chr = exp[i];
			if (chr != '+' && chr != '-' && chr != '*' && chr != '/'
					&& chr != '=')
				var1[j++] = chr;
		}
		ptr0 = soc_script_trim(var0);
		if (soc_script_val_get(scb, ptr0, &val0) != 0)
			return -1;
		ptr1 = soc_script_trim(var1);
		if (soc_script_val_get(scb, ptr1, &val1) != 0)
			return -1;

		if (strstr(exp, "+="))
			val0 += val1;
		else if (strstr(exp, "-="))
			val0 -= val1;
		else if (strstr(exp, "*="))
			val0 *= val1;
		else if (strstr(exp, "/="))
		{
			if (val1 == 0)
				return -1;
			val0 /= val1;
		}
		else if (strstr(exp, "="))
			val0 = val1;
		else
			return -1;

		return soc_script_val_set(scb, ptr0, &val0);
	}

	/* TODO:函数调用检查 */

	return -1;
}

int soc_script_val_get(soc_script_scb_t *scb, char *name, double *val)
{
	if (!name || !val)
		return -1;

	if (soc_script_io_get(name, val) == 0)/* IO */
		return 0;

	if (scb != NULL)
	{
		soc_script_var_t *var = soc_script_var_find(scb->head, name);/* SCB变量列表 */
		if (var != NULL)
		{
			if (var->type == SOC_SCRIPT_INT)
			{
				*val = var->val.v_int;
				return 0;
			}
			else if (var->type == SOC_SCRIPT_DOUBLE)
			{
				*val = var->val.v_double;
				return 0;
			}
			return -1;
		}
	}

	/* TODO:函数调用检查 */

	return soc_script_char2double(name, val);/* 数字转换 */
}

int soc_script_val_set(soc_script_scb_t *scb, char *name, double *val)
{
	if (!name || !val)
		return -1;

	if (soc_script_io_set(name, val) == 0)/* IO */
		return 0;

	if (scb != NULL)
	{
		soc_script_var_t *var = soc_script_var_find(scb->head, name);/* SCB变量列表 */
		if (var != NULL)
		{
			if (var->type == SOC_SCRIPT_INT)
			{
				var->val.v_int = (int) (*val);
				return 0;
			}
			else if (var->type == SOC_SCRIPT_DOUBLE)
			{
				var->val.v_double = *val;
				return 0;
			}
			return -1;
		}
	}

	return -1;
}

int soc_script_io_get(char *name, double *val)
{
	int n;
	int val32;
	char *ptr;

	if (!name || !val)
		return -1;

	if (strncmp("GPIO", name, 4) == 0)/* GPIO */
	{
		ptr = soc_script_trim(&name[4]);
		if (soc_script_char2bin(ptr, &n) != 0)
			return -1;

		if (soc_script_gpio_get(n, &val32) == 0)
		{
			*val = val32;
			return 0;
		}
	}
	else if (strncmp("ANALOG", name, 6) == 0)/* ANALOG */
	{
		ptr = soc_script_trim(&name[6]);
		if (soc_script_char2bin(ptr, &n) != 0)
			return -1;

		if (soc_script_analog_get(n, &val32) == 0)
		{
			*val = val32;
			return 0;
		}
	}
	else if (strncmp("OUTPUT", name, 6) == 0)/* OUTPUT */
	{
		ptr = soc_script_trim(&name[6]);
		if (soc_script_char2bin(ptr, &n) != 0)
			return -1;
		if (soc_script_output_get(n, &val32) == 0)
		{
			*val = val32;
			return 0;
		}
	}
	else if (strncmp("INPUT", name, 5) == 0)/* INPUT */
	{
		ptr = soc_script_trim(&name[5]);
		if (soc_script_char2bin(ptr, &n) != 0)
			return -1;

		if (soc_script_input_get(n, &val32) == 0)
		{
			*val = val32;
			return 0;
		}
	}

	return -1;
}

int soc_script_io_set(char *name, double *val)
{
	int n;
	char *ptr;

	if (!name || !val)
		return -1;

	if (strncmp("GPIO", name, 4) == 0)/* GPIO */
	{
		ptr = soc_script_trim(&name[4]);
		if (soc_script_char2bin(ptr, &n) != 0)
			return -1;
		return soc_script_gpio_set(n, (int ) *val);
	}
	else if (strncmp("OUTPUT", name, 6) == 0)/* OUTPUT */
	{
		ptr = soc_script_trim(&name[6]);
		if (soc_script_char2bin(ptr, &n) != 0)
			return -1;
		return soc_script_output_set(n, (int ) *val);
	}

	return -1;
}

int soc_script_scb_init(soc_script_scb_t *scb)
{
	if (scb == NULL)
		return -1;

	memset(scb, 0, sizeof(soc_script_scb_t));
	scb->stat = SOC_SCRIPT_STAT_INIT;
	scb->magic = SOC_SCRIPT_MAGIC;
	return 0;
}

int soc_script_destroy(soc_script_scb_t *scb)
{
	if (scb == NULL)
		return -1;

	if (scb->magic != SOC_SCRIPT_MAGIC)
		return -1;

	soc_script_var_destroy(&scb->head);
	return 0;
}
